/* ------------- 声明全局变量 ---------------- */

var data;  //棋盘格数据
var bonus;  //行棋合并数字得分奖励
var bonus_map = {  //合并的数字与对应的得分
    2: 2, // key: value
    4: 5,
    8: 10,
    16: 25,
    32: 50,
    64: 125,
    128: 250,
    256: 500,
    512: 1000,
    1024: 2000
};

/* ------------- 事件绑定 --------------*/

//对整个文档的按键事件进行绑定
$(document).on("keydown",function(e){
    move(e.keyCode);
});

// Try again? (重试？) 点击事件
$("header a").on("click",function(){
    init_game();
});

/* ------- 开始游戏 ----*/

init_game(); //初始化游戏


/* -------------- 函数定义 --------------------- */

// 初始化游戏
function init_game(){  
    data = [ // -1 表示空白
        [-1,-1,-1,-1],
        [-1,-1,-1,-1],
        [-1,-1,-1,-1],
        [-1,-1,-1,-1]
    ];
    bonus = 0;
    init_best(); //初始化最好成绩
    rand_fill_board(2); //随机填入2个数字（2或者4）
    //刷新棋盘
    refresh_board();
    display_score(); //显示分数
}


//---------- 最佳成绩（得分）相关函数 ---------------

/**
 * 初始化最佳得分
 */
function init_best() {
	if(get_best() === undefined)
		set_best(0);
}

/**
 * 设置最佳得分
 * @param {number} n - 得分
 */
function set_best(n){
    localStorage.setItem("2048best", n);
}

/**
 * 获取最佳得分
 */
function get_best(){
    var best = localStorage.getItem("2048best");
    return best;
}



/*
    用随机函数获取某个范围的整数:
    1、最小值（含）~最大值（含）
    Math.ceil(最小值 + Math.random()*(最大值-最小值));
    2、最小值（含）~最大值（不含）
    Math.floor(最小值 + Math.random()*(最大值-最小值));
*/
// 根据随机数获取 2 或 4 (2有80%机会，4有20%机会)
function get_2_or_4(){
    return Math.ceil(0 + Math.random()*(9-0))>7? 4 : 2;
}


/*
    将二维数组（每行等列）展开为一维数组，则二维坐标（行：row，列：col）与
    一维下标的对应关系是：
    行号*列数 + 行号 =>  下标
*/
/**
 * 将数据填入div
 * @param {number} r div所在的行
 * @param {number} c div所在的列
 * @param {number} num  要填入的数值
 */
function fill_cell(r,c,num){
    var e = $("#grid div")[r*4+c]; //行号*列数 + 行号
    $(e).text(num);
}

/**
 * 累加奖励分
 * @param {number} num - 得分
 */
function add_bonus(num){
    bonus += bonus_map[num];
}

/**
 * 获取棋盘盘面数字总和
 */
function get_sum(){
    var sum = 0;
    for(var r=0; r<4; r++){
        for(var c=0; c<4; c++){
            if(data[r][c]>0){
                sum += data[r][c];
            }
        }
    }
    return sum;
}

/**
 * 显示得分及最高得分
 */
function display_score(){
    var total = bonus + get_sum();
    $("#score span").text(total);
    if(get_best()<total){
        set_best(total);
    }
    $("#best-score").text(get_best());
}

/**
 * 随机填入2或4到空白格子
 * @param {number} count - 填入数字的数量
 */
function rand_fill_board(count){
    for(var i=0; i<count; i++){
        //得到2或者4
        var num = get_2_or_4();
        //找出空白格子的行列坐标
        blank_cells = [];
        for(var r=0; r<4; r++){
            for(var c=0; c<4; c++){
                if(data[r][c]===-1){
                    blank_cells.push([r,c]);
                }
            }
        }
        //如果有空白格子
        if(blank_cells.length>0){
            //随机挑出一个空白格子
            var idx = Math.floor(0 + Math.random()*(blank_cells.length-0));
            var rc = blank_cells[idx];
            //修改棋盘数字
            var row = rc[0]; 
            var col = rc[1];
            data[row][col] = num;
            
        }
    }
}

/**
 * 刷新棋盘显示
 */
function refresh_board(){
    for(r=0; r<4; r++){
        for(c=0; c<4; c++){
            var e = $("#grid div")[r*4+c]; //对应的div标签
            if(data[r][c]>0){
                fill_cell(r,c,data[r][c]); //填入数字到div
                $(e).attr('class','b'+data[r][c]); //修改div的class属性
            }
            else{
                fill_cell(r,c,""); //填入空白到div
                $(e).attr('class','bv'); //修改div的class属性
            }
        }
    }
}


/**
 * 在棋盘数据中找到某个指定的数
 * @param {number} num - 指定的要查找的数 
 * @returns {boolean}  true - 找到   false - 未找到
 */
function find_num(num){
    for(var r=0; r<4; r++){
        for(var c=0; c<4; c++){
            if(data[r][c]===num){
                return true;
            }
        }
    }
    return false;
}

/**
 * 检测能否再行棋
 * @returns {boolean}  true - 能    false - 不能
 */
function movable(){
    if(find_num(-1)) return true;
    for(var r=0; r<3; r++){
        for(var c=0; c<3; c++){
            if( (data[r+1][c]===data[r][c]) || 
                    data[r][c+1]===data[r][c] ){
                return true;
            }
        }
    }
    return false;
}

/**
 * 显示输赢
 */
function win_or_loss(){
    if(find_num(2048)){
        alert("恭喜恭喜，您赢了！");
        return;
    }
    if(!movable()){
        alert("对不起，您输了！！！");
    }           
}

/**
 * 行棋
 * @param {number} key - 按键编码
 */
function move(key){
    var tags = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]; //合并标记
    if(key==37){  // 左
        for(var c=3; c>0; c--){ //从最右边列开始
            for(var r=0; r<4; r++){ //从最上边行开始
                if(data[r][c]>0 && tags[r][c]==0){ //对应的单元格数字>0，并且不是合并得到的数字
                    if(data[r][c-1]===data[r][c]){ //按键方向相邻格子里的数字和当前格子里的数字相同
                        add_bonus(data[r][c]) //累加合并数字的奖励分
                        data[r][c] = -1; //当前格子数字设为-1 （清空）
                        data[r][c-1] *= 2; //相邻格子数字加倍（相当于合并）
                        tags[r][c-1] = 1; //相邻格子设置合并标志（1表示此格数字是合并而来）
                    }
                    else if(data[r][c-1]===-1){ //按键方向相邻格子里的数字==-1(空白)
                        data[r][c-1] = data[r][c]; // 相邻格子填入与当前格子相同的数字（相当于移动）
                        data[r][c] = -1; //当前格子数字设为-1 （清空）
                    }
                }
            }
        }
    }
    else if(key==38){ // 上
        for(var r=3; r>0; r--){
            for(var c=0; c<4; c++){
                if(data[r][c]>0 && tags[r][c]==0){
                    if(data[r-1][c]===data[r][c]){
                        add_bonus(data[r][c])
                        data[r][c] = -1;
                        data[r-1][c] *= 2;
                        tags[r-1][c] = 1; 
                    }
                    else if(data[r-1][c]===-1){
                        data[r-1][c] = data[r][c];
                        data[r][c] = -1;
                    }
                }
            }
        }
    }
    else if(key==39){ // 右
        for(var c=0; c<3; c++){
            for(var r=0; r<4; r++){
                if(data[r][c]>0 && tags[r][c]==0){
                    if(data[r][c+1]===data[r][c]){
                        add_bonus(data[r][c])
                        data[r][c] = -1;
                        data[r][c+1] *= 2;
                        tags[r][c+1] = 1; 
                    }
                    else if(data[r][c+1]===-1){
                        data[r][c+1] = data[r][c];
                        data[r][c] = -1;
                    }
                }
            }
        }
    }
    else if(key==40){ // 下
        for(var r=0; r<3; r++){
            for(var c=0; c<4; c++){
                if(data[r][c]>0 && tags[r][c]==0){
                    if(data[r+1][c]===data[r][c]){
                        add_bonus(data[r][c])
                        data[r][c] = -1;
                        data[r+1][c] *= 2;
                        tags[r+1][c] = 1; 
                    }
                    else if(data[r+1][c]===-1){
                        data[r+1][c] = data[r][c];
                        data[r][c] = -1;
                    }
                }
            }
        }    
    }
    display_score(); //显示当前得分
    rand_fill_board(1); //随机在空白格子中填入2或4
    refresh_board(); //更新棋盘显示
    win_or_loss(); //检查是否有输赢
}

